﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using FarseerPhysics.Factories;
using FarseerPhysics.Dynamics.Contacts;
using Microsoft.Xna.Framework.Input;
using RacePlatformer.Manager;
using Microsoft.Xna.Framework.Graphics;
using FarseerPhysics;
using FarseerPhysics.Dynamics.Joints;
using Dwarfs.Manager;
using FarseerPhysics.Common;

namespace RacePlatformer.Object
{
    public class BasePlayer : GameObject
    {
        private KeyboardState previouskeyboardstate;
        public enum PlayerState { RUNNING, JUMPING, DEAD, LANDED }
        public enum lastDirection { LEFT, RIGHT }
        public lastDirection lastdirection = lastDirection.RIGHT;
        public PlayerState playerState = PlayerState.JUMPING;
        protected Animation runanimation, jumpanimation, plofAnimation;
        protected RevoluteJoint feetbodyJoint;
        public Body feetBody;
        public Texture2D portrait;
        public Texture2D specialskill;
        public int ID = 0;
        public Keys playerKey;
        /// <summary>
        /// The base class of the player, all 'players'(i.e. daan and cassandra) derive from this class.
        /// </summary>
        /// <param name="maingame">The 'MainGame.cs' class</param>
        /// <param name="world">The world object</param>
        /// <param name="position">THe(start) position of the player(normal units)</param>
        /// <param name="playerKey">The key which makes the player jump</param>
        public BasePlayer(MainGame maingame, World world, Vector2 position, int ID, int levelID, Keys playerKey)
            : base(maingame, world, position, levelID)
        {
            LoadContent();
            this.body = BodyFactory.CreateRectangle(world, UnitsConverter.ToSimUnits(mainAnimation.frameWidth - 20), UnitsConverter.ToSimUnits(mainAnimation.frameHeight - 20), 1f, UnitsConverter.ToSimUnits(position));
            this.ID = ID;
            this.body.BodyType = BodyType.Dynamic;
            this.body.IgnoreGravity = false;
            this.body.OnCollision += Oncollision;
            this.body.LinearDamping = 2f;
            this.body.Restitution = 0.1f;
            this.body.Friction = 5f;
            this.layer = 0.4f;
            this.body.CollisionGroup = Manager.CollisionGroupManager.PLAYER;
            this.scale = 1.0f;
            this.playerKey = playerKey;
            this.rotation = 0.0f;
            feetBody = BodyFactory.CreateCircle(world, UnitsConverter.ToSimUnits(15), 1f, this.body.Position);
            feetBody.BodyType = BodyType.Dynamic;

            feetBody.Restitution = 0.0f;
            feetBody.Friction = 5f;
            feetbodyJoint = new RevoluteJoint(this.body, feetBody, new Vector2(0, UnitsConverter.ToSimUnits(15)), new Vector2(0, 0));
            feetBody.LinearDamping = 5f;
            feetbodyJoint.CollideConnected = false;
            feetBody.OnCollision += Oncollision;
            world.AddJoint(feetbodyJoint);
            // fix the angle of the feet (to prevent rolling down the hill, and rolling due to forward/backward momentum)
            JointFactory.CreateFixedAngleJoint(world, feetBody);
            // fix the angle of the players body (to prevent tilting)
            JointFactory.CreateFixedAngleJoint(world, body);

        }
        private bool Oncollision(Fixture fixtureA, Fixture fixtureB, Contact contact)
        {
            if (playerState == PlayerState.JUMPING)
            {
                if (fixtureB.CollisionGroup == Manager.CollisionGroupManager.GROUND)
                {
                    playerState = PlayerState.LANDED;
                }
            }

            //this is done because otherwise the same 'resetlevel' function will be called endlessly. 
            //because while its loading/unloading it still checks collision on the monster/player. So it will never stop
            //resetting the level
            if (!maingame.pManager.playerDied)
            {
                if (fixtureB.CollisionGroup == Manager.CollisionGroupManager.MONSTER)
                //or oother stuff
                {
                    playerState = PlayerState.DEAD;
                    maingame.pManager.playerDied = true;
                    maingame.pManager.resetLevel();
                }
            }
            return true;
        }
        /// <summary>
        /// The main update function for the player.(for each player)
        /// It checks keyboard/controller input and handles the different state(s) the player can be in.
        /// </summary>
        /// <param name="gametime"></param>
        public override void Update(Microsoft.Xna.Framework.GameTime gametime)
        {
            if (playerState == PlayerState.LANDED)
            {
                plofAnimation.update(gametime);
                if (plofAnimation.animationFinished) { playerState = PlayerState.RUNNING; plofAnimation.resetAnimation(); }
            }
            //update animation only if player is moving
            if ((this.body.LinearVelocity.X > 1 || this.body.LinearVelocity.X < -1))
            {
                mainAnimation.update(gametime);
            }
            else if (!mainAnimation.animationFinished)
            {
                mainAnimation.finishAnimation(gametime);
            }
            KeyboardState currentKeyBoardState = Keyboard.GetState();
            if (previouskeyboardstate.IsKeyUp(playerKey) && (currentKeyBoardState.IsKeyDown(playerKey)))
            {
                if (playerState != PlayerState.JUMPING)
                {
                    feetBody.ApplyLinearImpulse(new Vector2(0, PlayerManager.jumpPower));
                    playerState = BasePlayer.PlayerState.JUMPING;
                }
            }
            if (previouskeyboardstate.IsKeyDown(Keys.Right))
            {
                if (lastdirection == lastDirection.LEFT)
                {
                    this.feetBody.LinearVelocity = new Vector2(0, this.feetBody.LinearVelocity.Y);
                    this.desiredSpriteEffect = SpriteEffects.None;
                    lastdirection = lastDirection.RIGHT;
                }
                feetBody.LinearVelocity = new Vector2(PlayerManager.movementSpeed, this.feetBody.LinearVelocity.Y);
            }
            //move left
            if (previouskeyboardstate.IsKeyDown(Keys.Left))
            {
                if (lastdirection == lastDirection.RIGHT)
                {
                    this.body.LinearVelocity = new Vector2(0, this.body.LinearVelocity.Y);
                    this.desiredSpriteEffect = SpriteEffects.FlipHorizontally;
                    lastdirection = lastDirection.LEFT;
                }
                feetBody.LinearVelocity = new Vector2(-PlayerManager.movementSpeed, this.feetBody.LinearVelocity.Y);
            }
            previouskeyboardstate = currentKeyBoardState;
            base.Update(gametime);
        }
        /// <summary>
        /// Draws the current state's animation.
        /// </summary>
        /// <param name="gametime"></param>
        /// <param name="batch"></param>
        public override void Draw(GameTime gametime, Microsoft.Xna.Framework.Graphics.SpriteBatch batch)
        {
            switch (playerState)
            {
                case PlayerState.RUNNING:
                    setAnimation(runanimation);
                    break;
                case PlayerState.JUMPING:
                    setAnimation(jumpanimation);
                    break;
                case PlayerState.LANDED:
                    batch.Draw(plofAnimation.texture, new Vector2(UnitsConverter.ToDisplayUnits(this.body.Position.X), UnitsConverter.ToDisplayUnits(this.body.Position.Y) + 25), new Rectangle((int)(plofAnimation.currentFrame * plofAnimation.frameWidth), 0, (int)plofAnimation.frameWidth, (int)plofAnimation.frameHeight), Color.White, rotation, new Vector2(plofAnimation.frameWidth / 2, plofAnimation.frameHeight / 2), scale, desiredSpriteEffect, .399f);
                    setAnimation(runanimation);
                    break;
            }
            batch.Draw(mainAnimation.texture, new Vector2(UnitsConverter.ToDisplayUnits(this.body.Position.X), UnitsConverter.ToDisplayUnits(this.body.Position.Y) + 13), new Rectangle((int)(mainAnimation.currentFrame * mainAnimation.frameWidth), 0, (int)mainAnimation.frameWidth, (int)mainAnimation.frameHeight), Color.White, rotation, new Vector2(mainAnimation.frameWidth / 2, mainAnimation.frameHeight / 2), scale, desiredSpriteEffect, layer);
            //  base.Draw(gametime, batch);
        }
        protected virtual void LoadContent()
        {
            plofAnimation = new Animation(maingame, "player/plof_strip", 10, 50, 20, TimeSpan.FromSeconds(.02f), Animation.AnimationType.once);
        }
        /// <summary>
        /// Used to switch animations
        /// </summary>
        /// <param name="newAnimation"></param>
        private void setAnimation(Animation newAnimation)
        {
            mainAnimation = newAnimation;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns>The main body of the player.</returns>
        public Body getBody()
        {
            return this.body;
        }
    }
}
